﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SpLocalizer.Common.DbApi;
using System.Globalization;
using SpLocalizer.Common.Extensions;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;


namespace SpLocalizer.Common
{
    /// <summary>
    /// Functional sequence of translation work.
    /// It connected to particular datasource 
    /// 
    /// </summary>
    public abstract class AbstractTranslationProject 
    {

        #region Members
                /// <summary>
                /// Database entity provider
                /// </summary>
                public static SpLocalizerEntities DbEntities = new SpLocalizerEntities();
                /// <summary>
                /// Project data in the project settings datastore
                /// </summary>
                private Project ProjectData = null;

                private Guid ProjectId = Guid.Empty;

        #endregion

        #region Properties


            public string ConnectionString
            {
                get
                {                   
                    return this.ProjectData.ConnectionString;
                }
                set
                {
                    this.ProjectData.ConnectionString = value;
                }
            }


            public string ProjectTitle
            {
                get
                {
                    return this.ProjectData.ProjectTitle;
                }
                set
                {
                    this.ProjectData.ProjectTitle = value;
                }
            }

            protected List<AbstractContent> _ContentData = new List<AbstractContent>();
            /// <summary>
            /// Project content data
            /// </summary>
            public IEnumerable<AbstractContent> ContentData
        {
            get
            {
                return this._ContentData.AsEnumerable<AbstractContent>();
            }
        }
        #endregion
        

        #region Methods
        /// <summary>
        /// Create new project and load Content source basd on url
        /// </summary>
        /// <param name="provider"></param>
        /// <param name="ConnectionString"></param>
            public void CreateNewProject(string ConnectionString, bool IncludeDescendants)
        {
            this.ProjectData = new Project();
            this.ProjectData.ProjectId = Guid.NewGuid();
            this.ProjectData.ProjectDate = DateTime.Now;
            this.ProjectData.ConnectionString = ConnectionString;
            this.ProjectData.ProjectSettingsSerialization = this.GetType().Name;
            DbEntities.AddToProjects(this.ProjectData);
            this.Bind(IncludeDescendants);
            this.RegisterContentProviderTypes();            
        }

        /// <summary>
        /// Perform Binding to the content source
        /// </summary>
        protected abstract void Bind(bool IncludeDescendants);

        /// <summary>
        /// Opens existing project from datastore
        /// </summary>
        /// <param name="ProjectId"></param>
        /// <returns></returns>
        public static AbstractTranslationProject Open(Guid ProjectId)
        {
            // Load information from the database
            Project PersistentProjectData = DbEntities.Projects.Where<Project>(p => p.ProjectId == ProjectId)
                        .FirstOrDefault<Project>();
            if (PersistentProjectData == null)
            {
                throw new ApplicationException(String.Format("Cannot open project with id {0}",ProjectId));
            }

            AbstractTranslationProject theProject = 
                ServiceContainer.Resolve<AbstractTranslationProject>(PersistentProjectData.ProjectSettingsSerialization);

                theProject.ProjectData = PersistentProjectData;                
                theProject.ConnectionString = PersistentProjectData.ConnectionString;
                theProject.RegisterContentProviderTypes();
                theProject.ProjectId = ProjectId;
               // theProject.Bind();

                return theProject;
        }


        protected abstract AbstractContent LoadContent(LocalizableContent content);

        /// <summary>
        /// Run translation task for this project
        /// </summary>
        public void RunTranslate()
        {

        }

        /// <summary>
        /// Export all content translation task into content source
        /// All previosly created tasks for this content will be deleted
        /// </summary>
        /// <returns>New project Id</returns>
        public void ImportTranslationTasks(CultureInfo FromLanguage, CultureInfo ToLanguage)
        {
            //TODO: Clean 
            //TODO: Recursive content
          
            foreach (AbstractContent content in this.ContentData)
            {
                // Create database instance for this task
                LocalizableContent ContentData = new LocalizableContent();
                ContentData.Id = content.Id;
                
                ContentData.ParentContentId = content.ParentContentId;
                
                ContentData.ContentSourceProvider = content.ContentProviderName;
                ContentData.ContentId = content.ContentId;
                ContentData.ContentTitle = content.ContentTitle;
                ContentData.ContentAddress= content.ContentAddress;
                ContentData.ContentSourceProviderData = content.PropertiesSerializationXml;

                // Append new content into this project
                this.ProjectData.LocalizableContents.Add(ContentData);
                
                // Create list of translation tasks for this content
                IEnumerable<BaseTranslationTask> TaskToExport = content.BuildTranslationTasks(FromLanguage, ToLanguage);

                foreach (BaseTranslationTask Task in TaskToExport)
                {
                    TranslationTask TaskData = new TranslationTask();
                    TaskData.LoadFromAbstract(Task);
                                        
                    ContentData.TranslationTasks.Add(TaskData);
                }

               
                DbEntities.SaveChanges();

            }

        }

        /// <summary>
        /// Stop all translation tasks for this project
        /// </summary>
        public void StopTranslate()
        {
        }

        /// <summary>
        /// Register all content type provider types for conteainer
        /// </summary>
        /// <param name="RootContainer"></param>
        protected abstract void RegisterContentProviderTypes();

        /// <summary>
        /// Export all translations
        /// </summary>
        /// <param name="RootContainer"></param>
        public virtual void ExportTranslations()
        {
            // Select root level content to export  
                List<LocalizableContent> contentToExport =
                  DbEntities.LocalizableContents.Where(cnt => cnt.Project.ProjectId == this.ProjectId 
                                                                        && cnt.ParentContentId == null).ToList();

                if (contentToExport.Count == 0) // If we don't have root content - try to load all
                {
                    contentToExport = DbEntities.LocalizableContents.Where(cnt => cnt.Project.ProjectId == this.ProjectId)
                                                    .OrderBy(cnt => cnt.ParentContentId).ToList();
                }
             
             // Export content with all descendants (if any)
                foreach (LocalizableContent localizableContent in contentToExport)
                    ExportContentTranslations(localizableContent);

                PerformExportDone();
        }

        /// <summary>
        /// On export done event
        /// </summary>
        public virtual void PerformExportDone() { }

        protected void ExportContentTranslations(LocalizableContent localizableContent)
        {
          
                AbstractContent content = this.LoadContent(localizableContent);
                
                //Get all translation tasks for this content ready for Export (translated)
                if (!localizableContent.TranslationTasks.IsLoaded)
                    localizableContent.TranslationTasks.Load();

                List<TranslationTask> tasks =
                    localizableContent.TranslationTasks.Where(task => task.Status == (int)TranslationTaskStatus.Translated)
                                                                                                        .ToList<TranslationTask>();
                
                foreach (TranslationTask task in tasks)
                {
                    BaseTranslationTask taskToExport = task.MakeAbstractTask();
                        ServiceContainer.Resolve<BaseTranslationTask>(task.TaskTypeProvider);
                        try
                        {
                            content.ExportContentTranslations(taskToExport);
                            task.Status = (int)TranslationTaskStatus.Exported;
                        }
                        catch
                        {
                            task.Status = (int)TranslationTaskStatus.ExportError;
                        }
                }
                DbEntities.SaveChanges();

                //Export translations for child contents
                List<LocalizableContent> contentToExport =
                    DbEntities.LocalizableContents.Where(cnt => cnt.ParentContentId == localizableContent.ContentId 
                        && cnt.Project.ProjectId == this.ProjectId)
                                                                                                        .ToList<LocalizableContent>();

                if (contentToExport.Count() > 0)
                {
                    foreach (LocalizableContent childContent in contentToExport)    // recursion call                     
                            this.ExportContentTranslations(childContent);
                     
                }


                content.PerformExportDone();
        }


        #endregion
    }
}
